/**
* @describe 游戏资管理
* @author 游金宇(KM)
* @date 2023-08-03 17:40:51
*/

import { Asset, AssetManager, assetManager, Constructor, log, Prefab, resources } from 'cc';

type ProgressCallback = (completedCount: number, totalCount: number, item: any) => void;
type CompleteCallback<T> = ((err: Error | null, data: T) => void) | null
type IRemoteOptions = {
    [k: string]: any;
    ext?: string;
};
type AssetType<T = Asset> = Constructor<T>;


export class ResLoader {

    /**
     * 获取资源
     * @param path          资源路径
     * @param type          资源类型
     * @param bundleName    远程资源包名
     */
    get = <T extends Asset>(path: string, type?: AssetType<T> | null, bundleName: string = 'resources'): T | null => {
        var bundle: AssetManager.Bundle | null = assetManager.getBundle(bundleName);
        return bundle!.get(path, type);
    }

    /**
     * 获取资源类型
     */
    isAssetType = <T>(value: any): value is AssetType<T> => {
        return typeof value === 'function' && value.prototype instanceof Asset;
    }

    /**
     * 获取资源 
     * @param bundleName    远程包名
     * @param paths         资源路径
     * @param type          资源类型-
     * @param onProgress    加载进度回调
     * @param onComplete    加载完成回调
    */
    load<T extends Asset>(paths: string | string[]): void;
    load<T extends Asset>(paths: string | string[], type: AssetType<T>): void;
    load<T extends Asset>(paths: string, onComplete: CompleteCallback<T>): void;
    load<T extends Asset>(paths: string, type: AssetType<T>, onComplete: CompleteCallback<T>): void;
    load<T extends Asset>(paths: string, onProgress: ProgressCallback, onComplete: CompleteCallback<T>): void;
    load<T extends Asset>(paths: string, type: AssetType<T>, onProgress: ProgressCallback, onComplete: CompleteCallback<T>): void;
    load<T extends Asset>(paths: string[], onComplete: CompleteCallback<T[]>): void;
    load<T extends Asset>(paths: string[], onProgress: ProgressCallback, onComplete: CompleteCallback<T[]>): void;
    load<T extends Asset>(paths: string[], type: AssetType<T>, onComplete: CompleteCallback<T[]>): void;
    load<T extends Asset>(paths: string[], type: AssetType<T>, onProgress: ProgressCallback, onComplete: CompleteCallback<T[]>): void;

    load<T extends Asset>(bundleName: string, paths: string | string[]): void;
    load<T extends Asset>(bundleName: string, paths: string | string[], type: AssetType<T>): void;
    load<T extends Asset>(bundleName: string, paths: string, onComplete: CompleteCallback<T>): void;
    load<T extends Asset>(bundleName: string, paths: string[], onComplete: CompleteCallback<T[]>): void;
    load<T extends Asset>(bundleName: string, paths: string, type: AssetType<T>, onComplete: CompleteCallback<T>): void;
    load<T extends Asset>(bundleName: string, paths: string[], type: AssetType<T>, onComplete: CompleteCallback<T[]>): void;
    load<T extends Asset>(bundleName: string, paths: string, onProgress: ProgressCallback, onComplete: CompleteCallback<T>): void;
    load<T extends Asset>(bundleName: string, paths: string[], onProgress: ProgressCallback, onComplete: CompleteCallback<T[]>): void;
    load<T extends Asset>(bundleName: string, paths: string, type: AssetType<T>, onProgress: ProgressCallback, onComplete: CompleteCallback<T>): void;
    load<T extends Asset>(bundleName: string, paths: string[], type: AssetType<T>, onProgress: ProgressCallback, onComplete: CompleteCallback<T[]>): void;
    async load<T extends Asset>(...args: any[]): Promise<void> {
        let bundleName: string | null = null;
        let type: AssetType<T> | null = null;
        let onProgress: ProgressCallback | null = null;
        // 判断前两个参数是否都为字符串
        if (typeof args[0] === 'string' && typeof args[1] === 'string') {
            // 取出包名参数
            bundleName = args.shift();
        }
        // 取出路径参数
        let paths = args.shift()
        if ((typeof paths == 'string')) {
            paths as string
        } else {
            paths as string[]
        }

        // 取出类型参数
        if (this.isAssetType<T>(args[0])) {
            type = args.shift()
        }

        // 取出进度回调参数
        if (args.length == 2) {
            onProgress = args.shift()
        }
        // 取出完成回调参数
        const onComplete: CompleteCallback<typeof paths extends string ? T : T[]> = args.shift()

        let nonce: AssetManager.Bundle = resources

        if (bundleName && bundleName != 'resources') {
            // bundleName不存在
            if (!assetManager.bundles.has(bundleName)) {
                await this.loadBundle(bundleName)
            }
            let bundle = assetManager.bundles.get(bundleName)
            if (bundle) {
                nonce = bundle
            }
        }

        if (onProgress && onComplete) {
            nonce.load(paths, type, onProgress, onComplete)
        } else if (onComplete) {
            nonce.load(paths, type, onComplete)
        } else {
            nonce.load(paths, type)
        }
    }




    /**
     * 加载文件夹
     * @param bundleName    远程包名
     * @param dir           文件夹名
     * @param type          资源类型
     * @param onProgress    加载进度回调
     * @param onComplete    加载完成回调
     */

    loadDir<T extends Asset>(dir: string): void;
    loadDir<T extends Asset>(dir: string, type: AssetType<T>): void;
    loadDir<T extends Asset>(dir: string, onComplete: CompleteCallback<T[]>): void;
    loadDir<T extends Asset>(dir: string, type: AssetType<T>, onProgress: ProgressCallback, onComplete: CompleteCallback<T[]>): void;

    loadDir<T extends Asset>(bundleName: string, dir: string): void;
    loadDir<T extends Asset>(bundleName: string, dir: string, type: AssetType<T>): void;
    loadDir<T extends Asset>(bundleName: string, dir: string, onComplete: CompleteCallback<T[]>): void;
    loadDir<T extends Asset>(bundleName: string, dir: string, type: AssetType<T>, onProgress: ProgressCallback, onComplete: CompleteCallback<T[]>): void;
    async loadDir<T extends Asset>(...args: any[]): Promise<void> {
        let bundleName: string | null = null;
        let type: AssetType<T> | null = null;
        let onProgress: ProgressCallback | null = null;
        let onComplete: CompleteCallback<T[]> = null;
        // 获取bundleName是否存在
        // 判断前两个参数是否都为字符串
        if (typeof args[0] === 'string' && typeof args[1] === 'string') {
            bundleName = args.shift();
        }

        let paths: string = args.shift();

        if (this.isAssetType<T>(args[0])) {
            type = args.shift()
        }

        if (args.length == 2) {
            onProgress = args.shift()
        }
        onComplete = args.shift()

        let nonce: AssetManager.Bundle = resources

        if (bundleName && bundleName != 'resources') {
            // bundleName不存在
            if (!assetManager.bundles.has(bundleName)) {
                await this.loadBundle(bundleName)
            }
            let bundle = assetManager.bundles.get(bundleName)
            if (bundle) {
                nonce = bundle
            }
        }

        if (onProgress && onComplete) {
            nonce.loadDir(paths, type, onProgress, onComplete)
        } else if (onComplete) {
            nonce.loadDir(paths, type, onComplete)
        } else {
            nonce.loadDir(paths, type)
        }
    }


    /**
     * 加载远程资源
     * @param url           资源地址
     * @param options       资源参数，例：{ ext: ".png" }
     * @param onComplete    加载完成回调
     * 
     * res.ResLoader.loadRemote(params.iconv, (err: Error, imageAsset: ImageAsset) => {
        if (err) return error(err)
            if (isValid(this.itemSp)) {
                this.itemSp.spriteFrame = SpriteFrame.createWithImage(imageAsset)
            }
        })
     */
    loadRemote<T extends Asset>(url: string, options: IRemoteOptions, onComplete?: CompleteCallback<T>): void;
    loadRemote<T extends Asset>(url: string, onComplete?: CompleteCallback<T>): void;
    loadRemote<T extends Asset>(url: string, ...args: any): void {
        var options: IRemoteOptions | null = null;
        var onComplete: CompleteCallback<T> | null = null;
        if (args.length == 2) {
            options = args.shift()
        }
        onComplete = args.shift()
        assetManager.loadRemote<T>(url, { ext: ".png", ...options }, onComplete);
    }


    /**
     * 加载bundle
     * @param url       资源地址 <远程路径或者bundleName>
     * @param complete  完成事件
     * @param v         资源版本号
     * @example
     */
    loadBundle = (url: string, version?: string) => {
        return new Promise<AssetManager.Bundle>((resolve, reject) => {
            const onComplete = (err: Error | null, bundle: AssetManager.Bundle) => {
                if (err) {
                    return reject(err)
                }
                resolve(bundle);
            }
            version ?
                assetManager.loadBundle(url, { version }, onComplete) :
                assetManager.loadBundle(url, onComplete)
        });
    }

    /** 释放预制依赖资源 */
    releasePrefabtDepsRecursively = (uuid: string) => {
        var asset = assetManager.assets.get(uuid)!;
        assetManager.releaseAsset(asset);
        if (asset instanceof Prefab) {
            var uuids: string[] = assetManager.dependUtil.getDepsRecursively(uuid)!;
            uuids.forEach(uuid => {
                var asset = assetManager.assets.get(uuid)!;
                asset.decRef();
            });
        }
    }

    /**
     * 通过资源相对路径释放资源
     * @param path          资源路径
     * @param bundleName    远程资源包名
     */
    release = (path: string, bundleName: string = 'resources') => {
        var bundle = assetManager.getBundle(bundleName);
        if (bundle) {
            var asset = bundle.get(path);
            if (asset) {
                this.releasePrefabtDepsRecursively(asset._uuid);
            }
        }
    }

    /**
     * 通过相对文件夹路径删除所有文件夹中资源
     * @param path          资源文件夹路径
     * @param bundleName    远程资源包名
     */
    releaseDir = (path: string, bundleName: string = 'resources') => {
        var bundle: AssetManager.Bundle | null = assetManager.getBundle(bundleName);
        var infos = bundle?.getDirWithPath(path);
        infos?.map(info => {
            this.releasePrefabtDepsRecursively(info.uuid);
        });

        if (!path && bundleName != 'resources' && bundle) {
            assetManager.removeBundle(bundle);
        }
    }

    /** 打印缓存中所有资源信息 */
    dump = () => {
        assetManager.assets.forEach((value: Asset, key: string) => {
            log(assetManager.assets.get(key));
        });
        log(`当前资源总数:${assetManager.assets.count}`);
    }

    /**
    * 加载分包中的资源并提供进度反馈。
    * @param bundle - 已加载的分包。
    * @param onProgress - 进度回调函数。
    * @returns Promise<void> - 加载完成后的Promise。
    */
    loadAssetsWithProgress(bundle: AssetManager.Bundle, onProgress: (progress: number) => void): Promise<void> {
        return new Promise((resolve, reject) => {
            const assets = bundle.getDirWithPath('');
            const totalAssets = assets.length;
            let loadedAssets = 0;
            if (totalAssets === 0) {
                onProgress(1);
                resolve();
                return;
            }
            assets.forEach((asset) => {
                bundle.load(asset.path, (err) => {
                    if (err) {
                        reject(err);
                        return;
                    }
                    loadedAssets++;
                    onProgress(loadedAssets / totalAssets);
                    if (loadedAssets === totalAssets) {
                        resolve();
                    }
                });
            });
        });
    }

    /**扩展 TODO*/

}
